import flet as ft
import pyaudio
import wave
import threading
import time
import os
from datetime import datetime

class AudioRecorder:
    def __init__(self):
        self.is_recording = False
        self.frames = []
        self.audio = pyaudio.PyAudio()
        self.stream = None
        self.current_file = None
        self._lock = threading.Lock()  # 添加线程锁
        
    def start_recording(self):
        with self._lock:
            self.frames = []
            self.is_recording = True
            if self.stream is None:
                self.stream = self.audio.open(
                    format=pyaudio.paInt16,
                    channels=1,
                    rate=44100,
                    input=True,
                    frames_per_buffer=1024
                )
        
    def stop_recording(self):
        with self._lock:
            self.is_recording = False
            if self.stream is not None:
                try:
                    self.stream.stop_stream()
                    self.stream.close()
                except Exception as e:
                    print(f"关闭音频流时出错: {e}")
                finally:
                    self.stream = None
            self.save_recording()
        
    def save_recording(self):
        try:
            if not os.path.exists("recordings"):
                os.makedirs("recordings")
                
            if not self.frames:  # 如果没有录音数据，直接返回
                return
                
            filename = f"recordings/recording_{datetime.now().strftime('%Y%m%d_%H%M%S')}.wav"
            self.current_file = filename
            
            wf = wave.open(filename, 'wb')
            wf.setnchannels(1)
            wf.setsampwidth(self.audio.get_sample_size(pyaudio.paInt16))
            wf.setframerate(44100)
            wf.writeframes(b''.join(self.frames))
            wf.close()
        except Exception as e:
            print(f"保存录音时出错: {e}")
    
    def __del__(self):
        try:
            if self.stream is not None:
                self.stream.stop_stream()
                self.stream.close()
            if self.audio is not None:
                self.audio.terminate()
        except Exception as e:
            print(f"清理资源时出错: {e}")

def main(page: ft.Page):
    page.title = "录音软件"
    page.theme_mode = ft.ThemeMode.LIGHT
    page.window.width = 400
    page.window.height = 600
    page.padding = 20
    
    recorder = AudioRecorder()
    recording_thread = None
    timer_thread = None
    
    # 状态显示
    status_text = ft.Text("就绪", size=20, text_align=ft.TextAlign.CENTER)
    timer_text = ft.Text("00:00", size=30, text_align=ft.TextAlign.CENTER)
    
    def update_timer():
        seconds = 0
        while recorder.is_recording:
            try:
                minutes = seconds // 60
                secs = seconds % 60
                timer_text.value = f"{minutes:02d}:{secs:02d}"
                page.update()
                time.sleep(1)
                seconds += 1
            except Exception as e:
                print(f"计时器更新出错: {e}")
                break
    
    def record_audio():
        while recorder.is_recording:
            try:
                with recorder._lock:
                    if recorder.stream and recorder.is_recording:
                        data = recorder.stream.read(1024, exception_on_overflow=False)
                        recorder.frames.append(data)
            except Exception as e:
                print(f"录音时出错: {e}")
                break
    
    def toggle_recording(e):
        nonlocal recording_thread, timer_thread
        
        if not recorder.is_recording:
            # 开始录音
            try:
                recorder.start_recording()
                status_text.value = "录音中..."
                record_btn.text = "停止录音"
                record_btn.icon = ft.Icons.STOP
                record_btn.style.bgcolor = ft.Colors.RED_600
                
                # 开启新线程
                recording_thread = threading.Thread(target=record_audio, daemon=True)
                timer_thread = threading.Thread(target=update_timer, daemon=True)
                recording_thread.start()
                timer_thread.start()
            except Exception as e:
                print(f"开始录音时出错: {e}")
                status_text.value = "录音启动失败"
                recorder.is_recording = False
        else:
            # 停止录音
            try:
                recorder.stop_recording()
                status_text.value = "录音已保存"
                record_btn.text = "开始录音"
                record_btn.icon = ft.Icons.MIC
                record_btn.style.bgcolor = ft.Colors.GREEN_600
                timer_text.value = "00:00"
                
                # 等待线程结束
                if recording_thread and recording_thread.is_alive():
                    recording_thread.join(timeout=1.0)
                if timer_thread and timer_thread.is_alive():
                    timer_thread.join(timeout=1.0)
                    
                update_files_list()
            except Exception as e:
                print(f"停止录音时出错: {e}")
                status_text.value = "录音停止失败"
        
        page.update()
    
    # 创建录音按钮
    record_btn = ft.ElevatedButton(
        "开始录音",
        icon=ft.Icons.MIC,
        on_click=toggle_recording,
        style=ft.ButtonStyle(
            color=ft.Colors.WHITE,
            bgcolor=ft.Colors.GREEN_600,
            padding=20,
        ),
        width=200
    )
    
    # 创建录音文件列表
    files_list = ft.ListView(
        expand=True,
        spacing=10,
        padding=20,
        height=200
    )
    
    def update_files_list():
        try:
            files_list.controls.clear()
            if os.path.exists("recordings"):
                for file in sorted(os.listdir("recordings"), reverse=True):
                    if file.endswith(".wav"):
                        files_list.controls.append(
                            ft.Card(
                                content=ft.Container(
                                    content=ft.Text(file),
                                    padding=10
                                )
                            )
                        )
            page.update()
        except Exception as e:
            print(f"更新文件列表时出错: {e}")
    
    # 更新文件列表按钮
    refresh_btn = ft.IconButton(
        icon=ft.Icons.REFRESH,
        on_click=lambda _: update_files_list()
    )
    
    # 布局
    page.add(
        ft.Column(
            [
                ft.Container(
                    content=status_text,
                    alignment=ft.alignment.center,
                    padding=20
                ),
                ft.Container(
                    content=timer_text,
                    alignment=ft.alignment.center,
                    padding=20
                ),
                ft.Row(
                    [record_btn],
                    alignment=ft.MainAxisAlignment.CENTER,
                ),
                ft.Divider(height=40, thickness=2),
                ft.Row(
                    [
                        ft.Text("录音文件列表", size=16, weight=ft.FontWeight.BOLD),
                        refresh_btn
                    ],
                    alignment=ft.MainAxisAlignment.SPACE_BETWEEN
                ),
                files_list
            ],
            spacing=10,
            alignment=ft.MainAxisAlignment.START
        )
    )
    
    # 初始化文件列表
    update_files_list()
    
    def handle_window_event(e):
        if e.data == "close":
            page.dialog = confirm_dialog
            confirm_dialog.open = True
            page.update()

    def yes_click(e):
        try:
            # 停止录音
            if recorder.is_recording:
                recorder.stop_recording()
            
            # 强制设置录音状态为False
            recorder.is_recording = False
            
            # 关闭音频流
            if recorder.stream is not None:
                recorder.stream.stop_stream()
                recorder.stream.close()
                recorder.stream = None
            
            # 终止音频设备
            if recorder.audio is not None:
                recorder.audio.terminate()
                recorder.audio = None
            
            # 等待线程结束
            if recording_thread and recording_thread.is_alive():
                recording_thread.join(timeout=0.5)
            if timer_thread and timer_thread.is_alive():
                timer_thread.join(timeout=0.5)
            
            page.window.destroy()
        except Exception as e:
            print(f"关闭窗口时出错: {e}")
            page.window.destroy()

    def no_click(e):
        confirm_dialog.open = False
        page.update()

    confirm_dialog = ft.AlertDialog(
        modal=True,
        title=ft.Text("确认退出"),
        content=ft.Text("确定要退出录音软件吗？"),
        actions=[
            ft.ElevatedButton("确定", on_click=yes_click),
            ft.OutlinedButton("取消", on_click=no_click),
        ],
        actions_alignment=ft.MainAxisAlignment.END,
    )

    page.window.prevent_close = True
    page.window.on_event = handle_window_event

if __name__ == "__main__":
    ft.app(target=main)